﻿#include "tools.h"

/**
 * @brief 工具相关初始化 目前主要检查了大小端存储的配置是否正确
 */
net_err_t tools_init()
{
    DBG_INFO(DBG_TOOLS_LEVEL, "tools init begin!");

    if (NET_ENDIAN_LITTLE != is_little_endian())
    {
        DBG_ERROR(DBG_TOOLS_LEVEL, "the param of little:%d is error!", NET_ENDIAN_LITTLE);
        return NET_ERR_PARAM;
    }

    DBG_INFO(DBG_TOOLS_LEVEL, "tools init end!");

    return NET_ERR_OK;
}

/**
 * @brief 用于对数据包的校验和进行检验
 * @param buf 校验的数据的起始地址
 * @param len 校验的数据的长度,单位字节
 * @param pre_sum 之前算出的校验和
 * @param complement 是否对校验和取反
 * @return 计算出的校验和
 */
uint16_t tools_checksum16(uint32_t offset, const void *buf, uint16_t len, uint32_t pre_sum, bool complement)
{
    uint16_t *curr_buf = (uint16_t *)buf;
    uint32_t check_sum = pre_sum;

    // 起始字节不对齐，加高到8位
    if (offset & 0x1)
    {
        uint8_t *buf = (uint8_t *)curr_buf;
        check_sum += *buf++ << 8;
        curr_buf = (uint16_t *)buf;
        --len;
    }

    while (len > 1)
    {
        check_sum += *curr_buf++;
        len -= 2;
    }

    if (len > 0)
    {
        check_sum += *(uint8_t *)curr_buf;
    }

    uint16_t high;
    while ((high = check_sum >> 16) != 0)
    {
        check_sum = high + (check_sum & 0xffff);
    }

    return complement ? (uint16_t)~check_sum : (uint16_t)check_sum;
}

/**
 * @brief 用于udp或者tcp计算校验和
 */
uint16_t tools_checksum_peso(pktbuf_t *buf, const ipaddr_t *dst_ip, const ipaddr_t *src_ip, uint8_t protocol)
{
    uint8_t zero_protocol[2] = {0, protocol};
    uint16_t total_len = x_htons(buf->total_size);
    uint32_t offset = 0;
    uint16_t checksum = 0;

    checksum = tools_checksum16(offset, src_ip->addr, IPV4_ADDR_SIZE, checksum, false);
    offset += IPV4_ADDR_SIZE;
    checksum = tools_checksum16(offset, dst_ip->addr, IPV4_ADDR_SIZE, checksum, false);
    offset += IPV4_ADDR_SIZE;
    checksum = tools_checksum16(offset, zero_protocol, 2, checksum, false);
    offset += 2;
    checksum = tools_checksum16(offset, &total_len, 2, checksum, false);
    offset += 2;
    pktbuf_reset_acc(buf);
    checksum = pktbuf_checksum16(buf, buf->total_size, checksum, true);

    return checksum;
}